// 看了就会，手写 Promise 原理，最通俗易懂的版本
// https://juejin.cn/post/6994594642280857630

class MyPromise {
    constructor(executor) {
        this.initValue();
        this.initBind();
        try {
            executor(this.resolve, this.reject);
        } catch (e) {
            this.reject(e);
        }
    }

    initBind() {
        this.resolve = this.resolve.bind(this);
        this.reject = this.reject.bind(this);
    }

    initValue() {
        this.PromiseResult = null;
        this.PromiseState = 'pending';
        this.onFulfilledCallbacks = [];
        this.onRejectedCallbacks = [];
    }

    resolve(value) {
        if (this.PromiseState !== 'pending') return;
        this.PromiseState = 'fulfilled';
        this.PromiseResult = value;

        while (this.onFulfilledCallbacks.length) {
            this.onFulfilledCallbacks.shift()(this.PromiseResult);
        }
    }

    reject(reason) {
        if (this.PromiseState !== 'pending') return;
        this.PromiseState = 'rejected';
        this.PromiseResult = reason;

        while (this.onRejectedCallbacks.length) {
            this.onRejectedCallbacks.shift()(this.PromiseResult);
        }
    }

    then(onFulfilled, onRejected) {
        onFulfilled =
            typeof onFulfilled === 'function' ? onFulfilled : (val) => val;
        onRejected =
            typeof onRejected === 'function'
                ? onRejected
                : (reason) => {
                      throw reason;
                  };
        if (this.PromiseState === 'fulfilled') {
            onFulfilled(this.PromiseResult);
        } else if (this.PromiseState === 'rejected') {
            onRejected(this.PromiseResult);
        } else if (this.PromiseState === 'pending') {
            this.onFulfilledCallbacks.push(onFulfilled.bind(this));
            this.onRejectedCallbacks.push(onRejected.bind(this));
        }
    }
}

const test1 = new MyPromise((resolve, reject) => {
    setTimeout(() => {
        // reject('失败');
        resolve('success 42');
    }, 1000);
}).then(
    (res) => {
        console.log(res);
    },
    (err) => {
        console.log(err);
    }
);
